/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Velocity4Net.Runtime.Directives
{
    /**
     * This handles context scoping and metadata for directives.
     *
     * @author Nathan Bubna
     * @version $Id$
     */
    public class Scope : DictionaryBase
    {
        private Object replaced;
        private Scope parent;
        private Info info;
        protected Object owner;

        public Scope(Object owner, Object previous)
        {
            this.owner = owner;
            if (previous != null)
            {
                try
                {
                    this.parent = (Scope)previous;
                }
                catch (System.Exception cce)
                {
                    this.replaced = previous;
                }
            }
        }

        public Object this[Object key]
        {
            get
            {
                Object o = base.Dictionary[key];
                if (o == null && parent != null && !base.Dictionary.Contains(key))
                {
                    return parent[key];
                }
                return o;
            }
        }

        public Object Add(Object key, Object value)
        {
            this.Dictionary.Add(key, value);
            return value;
        }

        /**
         * Allows #stop to easily trigger the proper StopCommand for this scope.
         */
        internal void Stop()
        {
            throw new StopCommand(owner);
        }

        /**
         * Returns the number of control arguments of this type
         * that are stacked up.  This is the distance between this
         * instance and the topmost instance, plus one. This value
         * will never be negative or zero.
         */
        protected int Depth
        {
            get
            {
                if (parent == null)
                {
                    return 1;
                }
                return parent.Depth + 1;
            }
        }

        /**
         * Returns the topmost parent control reference, retrieved
         * by simple recursion on {@link #getParent}.
         */
        public Scope Topmost
        {
            get
            {
                if (parent == null)
                {
                    return this;
                }
                return parent.Topmost;
            }
        }

        /**
         * Returns the parent control reference overridden by the placement
         * of this instance in the context.
         */
        public Scope Parent
        {
            get
            {
                return parent;
            }
        }

        /**
         * Returns the user's context reference overridden by the placement
         * of this instance in the context.  If there was none (as is hoped),
         * then this will return null.  This never returns parent controls;
         * those are returned by {@link #getParent}.
         */
        public Object Replaced
        {
            get
            {
                if (replaced == null && parent != null)
                {
                    return parent.Replaced;
                }
                return replaced;
            }
        }

        /**
         * Returns info about the current scope for debugging purposes.
         */
        public Info ScopeInfo
        {
            get
            {
                if (info == null)
                {
                    info = new Info(this, owner);
                }
                return info;
            }
        }

        /**
         * Class to encapsulate and provide access to info about
         * the current scope for debugging.
         */
        public class Info
        {
            private Scope scope;
            private Directive directive;
            private Template template;

            public Info(Scope scope, Object owner)
            {
                if (owner is Directive)
                {
                    directive = (Directive)owner;
                }
                if (owner is Template)
                {
                    template = (Template)owner;
                }
                this.scope = scope;
            }

            public String Name
            {
                get
                {
                    if (directive != null)
                    {
                        return directive.Name;
                    }
                    if (template != null)
                    {
                        return template.Name;
                    }
                    return null;
                }
            }

            public String Type
            {
                get
                {
                    if (directive != null)
                    {
                        switch (directive.Type)
                        {
                            case 1:
                                return "block";
                            case 2:
                                return "line";
                        }
                    }
                    if (template != null)
                    {
                        return template.CharSet;
                    }
                    return null;
                }
            }

            public int Depth
            {
                get
                {
                    return scope.Depth;
                }
            }

            public String TemplateName
            {
                get
                {
                    if (directive != null)
                    {
                        return directive.TemplateName;
                    }
                    if (template != null)
                    {
                        return template.Name;
                    }
                    return null;
                }
            }

            public int Line
            {
                get
                {
                    if (directive != null)
                    {
                        return directive.Line;
                    }
                    return 0;
                }
            }

            public int Column
            {
                get
                {
                    if (directive != null)
                    {
                        return directive.Column;
                    }
                    return 0;
                }
            }

            public override String ToString()
            {
                StringBuilder sb = new StringBuilder();
                if (directive != null)
                {
                    sb.Append('#');
                }
                sb.Append(Name);
                sb.Append("[type:").Append(this.Type);
                int depth = Depth;
                if (depth > 1)
                {
                    sb.Append(" depth:").Append(depth);
                }
                if (template == null)
                {
                    String vtl = TemplateName;
                    sb.Append(" template:");
                    if (vtl.IndexOf(" ") < 0)
                    {
                        sb.Append(vtl);
                    }
                    else
                    {
                        sb.Append('"').Append(vtl).Append('"');
                    }
                    sb.Append(" line:").Append(Line);
                    sb.Append(" column:").Append(Column);
                }
                sb.Append(']');
                return sb.ToString();
            }
        }

    }
}